package com.ruoyi.common.flowable;

import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cmd.AbstractDynamicInjectionCmd;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


/**
 * @author 艾付强
 * 动态增加任务节点
 */
@Slf4j
public class AfterSignUserTaskCmd extends AbstractDynamicInjectionCmd implements Command<Void> {
    //流程实例id
    protected String processInstanceId;

    //后加签的节点信息
    private final DynamicUserTaskBuilder signUserTaskBuilder;

    private Process process;

    private BpmnModel bpmnModel;

    private CommandContext commandContext;

    private String prevActivityId;

    public AfterSignUserTaskCmd(String prevActivityId, String processInstanceId, DynamicUserTaskBuilder signUserTaskBuilder) {
        this.processInstanceId = processInstanceId;
        this.prevActivityId = prevActivityId;
        this.signUserTaskBuilder = signUserTaskBuilder;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        //AbstractDynamicInjectionCmd提供的修改方法入口
        createDerivedProcessDefinitionForProcessInstance(commandContext, processInstanceId);
        return null;
    }

    @Override
    protected void updateBpmnProcess(CommandContext commandContext, Process process,
                                     BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {
        this.process = process;
        this.bpmnModel = bpmnModel;
        this.commandContext = commandContext;

        FlowElement previousFlowElement = process.getFlowElement(prevActivityId, true);
        Assert.isFalse(!(previousFlowElement instanceof Task), "task type error");

        Activity previousActivity = (Activity) previousFlowElement;
        addUserTask(previousActivity);

        new BpmnAutoLayout(bpmnModel).execute();
        //clearBpmnModel();
        //当前流程的重新定义
        BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
    }

    private Activity addUserTask(Activity previousActivity) {
        //由于是后加签所以需要获取当前流程节点流出的sequence
        List<String> userIdList = Arrays.asList(signUserTaskBuilder.getAssignee().split(","));

        SequenceFlow currentTaskOutSequenceFlow = previousActivity.getOutgoingFlows().get(0);

        //定义新的节点
        UserTask addUserTask = new UserTask();

        //流程的id 可以在调用的时候传入 也可以根据flowable提供的方法生成
        String id = signUserTaskBuilder.getId();
        addUserTask.setId(id);
        addUserTask.setName(signUserTaskBuilder.getName());
        addUserTask.setAssignee(userIdList.get(0));

        process.addFlowElement(addUserTask);

        //定义新的sequence 将节点的source改为新的节点  target 改为当前节点的target
        SequenceFlow newSequenceFlow = this.createSequenceFlow(addUserTask.getId(), currentTaskOutSequenceFlow.getTargetFlowElement().getId());
        process.addFlowElement(newSequenceFlow);

        previousActivity.setOutgoingFlows(Collections.singletonList(newSequenceFlow));
        //修改原有sequence 将原来的sequence的target改为新的节点
        currentTaskOutSequenceFlow.setTargetFlowElement(addUserTask);
        currentTaskOutSequenceFlow.setTargetRef(addUserTask.getId());
        return addUserTask;
    }

    //clean清除原有的一些图的坐标信息
    private void clearBpmnModel() {
        bpmnModel.getLocationMap().clear();
        bpmnModel.getFlowLocationMap().clear();
        bpmnModel.getLabelLocationMap().clear();
    }

    @Override
    protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity,
                                    ExecutionEntity executionEntity, List<ExecutionEntity> list) {

    }

    //这个方法也可以单独写在一个工具类里
    public SequenceFlow createSequenceFlow(String from, String to) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        flow.setId("sequenceFlow-" + CommandContextUtil.getProcessEngineConfiguration(commandContext).getIdGenerator().getNextId());
        return flow;
    }
}